package model;

import java.util.UUID;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

import DAO.SessionDAO;

import common.Enums.MethodType;
import common.Exceptions.DAOException;
import common.Exceptions.FormulaException;
import common.Exceptions.MissingParameterException;
import common.Utils.LinkedSet;
import common.Utils.StringUtils;

public class Formula 
{
	private UUID ID;
	private String formula;
	private LinkedSet<Criterion> parameters;
	private String  MISSING_PARAMETER = "UNDEFINED";
	
	public Formula()
	{
		ID = UUID.randomUUID();
	}
	
	public Formula(UUID ID, String formula, LinkedSet<Criterion> parameters)
	{
		this.ID = ID;
		setFormula(formula, parameters);
	}
	
	public Formula(String formula, LinkedSet<Criterion> parameters) 
	{
		ID = UUID.randomUUID();
		setFormula(formula, parameters);
	}
	
	public UUID getID()
	{
		return ID;
	}
	
	public String getFormula() 
	{
		String retVal = formula;
		
		for(Criterion crt : parameters)
			retVal = retVal.replace(crt.getID().toString(), crt.getName());
		
		return retVal;
	}
	
	private void setFormula(String formula)
	{
		for(Criterion crt : parameters)
			formula = formula.replace(crt.getName(), crt.getID().toString());
		
		this.formula = formula;
	}
	
	public void setFormula(String formula, LinkedSet<Criterion> parameters)
	{
		this.parameters = parameters;
		setFormula(formula);
	}
	
	public LinkedSet<Criterion> getParameters()
	{
		return parameters;
	}

	public Value calculateResult(QuantitativeValue result, Case caso, UUID parentID) throws FormulaException, MissingParameterException, DAOException
	{
		String expression = formula;
		if(result == null)
			result = new QuantitativeValue();
		
		for(Criterion crt : parameters)
		{
			Method method = SessionDAO.getInstance().getMethodApplication(crt, caso, parentID).getMethod();
			if(method != null && (method.getType() == MethodType.QUANTITATIVE || method.getType() == MethodType.CALCULATION))
			{
				Value val = SessionDAO.getInstance().getRelatedValue(crt.getID(), caso.getID());
				if(val instanceof QuantitativeValue)
				{
					Double crtValue = ((QuantitativeValue)val).getResult();
					expression = expression.replace(crt.getID().toString(), crtValue.toString());
				}
				else
					throw new FormulaException("The value of the criterion '" + crt.getName() + "' is a qualitative value. Expression cannot be evaluated.");
			}
			else if(method == null)
				throw new FormulaException("The criterion '" + crt.getName() + "' has no value.");
		}
		
		ScriptEngineManager sem = new ScriptEngineManager();
		ScriptEngine e = sem.getEngineByName("ECMAScript");
		e.getFactory();

		if(StringUtils.containsLetter(expression))
		{
			result.setHasError(true);
			if(expression.contains(MISSING_PARAMETER))
				expression = expression.replaceAll(MISSING_PARAMETER, "1");
		}
		
		try 
		{
			result.setResult(Double.parseDouble((e.eval(expression)).toString()));
		} 
		catch (Exception e1) 
		{
			e1.printStackTrace();
			throw new FormulaException("There was an error while trying to calculate the result: " + e1.getMessage());
		} 
			
		return result;
	}
	
	public static boolean isFormulaValid(String formula)
	{
		boolean isValid = true;
		
		//TODO: Matches with some regular expression...
		
		return isValid;
	}

	public void addParamater(Criterion parameter) 
	{
		if(parameter != null)
			parameters.add(parameter);
	}
	
	public void removeParameter(UUID paramID)
	{
		for(int i = parameters.size() - 1; i >= 0 ; i--)
			if(parameters.get(i).getID().equals(paramID))
			{
				formula = formula.replace(paramID.toString(), MISSING_PARAMETER);
				parameters.remove(i);
				break;
			}
	}
}
